package com.laiyw.monitor.core.context;

import com.laiyw.monitor.core.commons.Constants;
import com.laiyw.monitor.core.annotation.Autowired;
import com.laiyw.monitor.core.annotation.Controller;
import com.laiyw.monitor.core.annotation.RequestMapping;
import com.laiyw.monitor.core.annotation.Service;
import com.laiyw.monitor.utils.ScanUtils;
import com.laiyw.monitor.utils.StringUtils;

import javax.servlet.ServletContext;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

/**
 * @Description:
 * @Auther: Laiyw
 * @Date: 2018/10/5 13:55
 */
public class ContextLoader {
    protected static final Logger logger = Logger.getLogger("contextloader");
    protected Enum SYSTEM_TYPE = StringUtils.getOS();
    private Map<String, Object> beansMap = new HashMap<>();

    public synchronized void initServiceBeans(ServletContext servletContext) {
        logger.info(" - The current operating system is " + SYSTEM_TYPE.name().toLowerCase());
        Set<Class<?>> classSet = ScanUtils.scan(Constants.SCAN_PACKAGENAME);
        if (null == classSet || classSet.isEmpty())
            return;
        Iterator<Class<?>> iterator = classSet.iterator();
        Class clazz;
        while (iterator.hasNext()) {
            clazz = iterator.next();
            if (isCurrOS(clazz) && checkClassType(clazz) && isAnnotation(clazz.getAnnotations())) {
                try {
                    beansMap.put(clazz.getSimpleName(), clazz.newInstance());
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        injection();
        initRequestController(servletContext);
    }

    public void initRequestController(ServletContext servletContext) {
        Map<String, InvokeInfo> controllerMap = new HashMap<>();
        for (String key : beansMap.keySet()) {
            if (null != beansMap.get(key).getClass().getAnnotation(Controller.class)) {
                controllerMap.putAll(findRequests(beansMap.get(key)));
            }
        }
        servletContext.setAttribute(Constants.MONITOR_REQUEST_MAP, controllerMap);
    }

    private boolean isCurrOS(Class clazz) {
        if (null != clazz.getAnnotation(Controller.class))
            return true;
        Service service = (Service) clazz.getAnnotation(Service.class);
        if (null != service) {
            if (service.value() == SYSTEM_TYPE) {
                return true;
            }
        }
        return false;
    }

    private Map<String, InvokeInfo> findRequests(Object instance) {
        Map<String, InvokeInfo> controllerMap = new HashMap<>();
        Method[] methods = instance.getClass().getDeclaredMethods();
        RequestMapping requestMapping;
        for (Method method : methods) {
            requestMapping = method.getAnnotation(RequestMapping.class);
            if (null != requestMapping) {
                controllerMap.put(requestMapping.value(), new InvokeInfo(requestMapping.value(), instance, method, method.getParameters()));
            }
        }
        return controllerMap;
    }

    private void injection() {
        for (String key : beansMap.keySet()) {
            setParameter(beansMap.get(key));
        }
    }

    private void setParameter(Object instance) {
        Field[] fields = instance.getClass().getDeclaredFields();
        if (null == fields || fields.length == 0)
            return;
        try {
            for (Field field : fields) {
                if (null != field.getAnnotation(Autowired.class)) {
                    set(field, instance);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void set(Field field, Object instance) throws IllegalAccessException, InstantiationException {
        Class bean = getBean(field);
        if (null == bean)
            return;
        Service service = (Service) bean.getAnnotation(Service.class);
        if (service.value() == SYSTEM_TYPE) {
            field.setAccessible(true);
            field.set(instance, beansMap.get(bean.getSimpleName()));
        }
    }

    private Class getBean(Field field) {
        Class bean;
        for (String key : beansMap.keySet()) {
            bean = beansMap.get(key).getClass();
            if (isAchieve(bean, field.getType())) {
                return bean;
            }
        }
        return null;
    }

    private boolean isAchieve(Class bean, Class fieldType) {
        if (bean.getSimpleName().equals(fieldType.getSimpleName()))
            return true;
        Class[] classes = bean.getInterfaces();
        if (null != classes && classes.length != 0) {
            for (Class c : classes) {
                if (c.getSimpleName().equals(fieldType.getSimpleName())) {
                    return true;
                }
            }
        } else {
            if (bean.getSuperclass().getSimpleName().equals(fieldType.getSimpleName()))
                return true;
        }
        return false;
    }

    private boolean isAnnotation(Annotation[] annotations) {
        for (Annotation annotation : annotations) {
            if (annotation instanceof Controller || annotation instanceof Service)
                return true;
        }
        return false;
    }

    private boolean checkClassType(Class clazz) {
        if (clazz.isInterface() || clazz.isAnnotation() || clazz.isEnum())
            return false;
        return true;
    }
}
